package com.cskaoyan.utils;

import com.cskaoyan.bean.user.User;
import com.cskaoyan.bean.wx_user.exception.UserNoLoginException;
import lombok.SneakyThrows;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Component;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * 类<code>Doc</code>用于：TODO 反射赋值javabean工具类
 *
 * @author Acher
 * @version 1.0
 * @date 2021-10-19
 */
@Component
public class ReflectionSetJavaBeanUtils<T> {

    /**
     * 方法<code>setJavaBean</code>作用为：通过反射set对应javabean的
     * addTime、updateTime、deleted属性
     *
     * @param bean
     * @return void
     * @throws
     * @author Acher
     */
    @SneakyThrows
    public static <T> void setJavaBean(T bean) {
        Class<?> clazz = bean.getClass();
        Field[] fields = clazz.getDeclaredFields();
        Date date = new Date(System.currentTimeMillis());
        for (Field field : fields) {
            String name = field.getName();
            field.setAccessible(true);
            if (("addTime".equals(name) && field.get(bean) == null) || "updateTime".equals(name)) {
                invoke(bean, clazz, name, date);
            }
            if ("deleted".equals(name)) {
                invoke(bean, clazz, name, false);
            }
            if ("userId".equals(name)) {
                PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
                if (principals == null)
                    throw new UserNoLoginException();
                User principal = (User) principals.getPrimaryPrincipal();
                invoke(bean, clazz, name, principal.getId());
            }
            if ("checked".equals(name)) {
                invoke(bean, clazz, name, false);
            }
        }
    }

    private static <T> void invoke(T bean, Class<?> clazz, String name, Object obj) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
        new PropertyDescriptor(name, clazz)
                .getWriteMethod()
                .invoke(bean, obj);
    }

    @SneakyThrows
    public static <T> void setBeanDeletedValue(T bean) {
        Class<?> clazz = bean.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String name = field.getName();
            field.setAccessible(true);
            if ("deleted".equals(name)) {
                invoke(bean, clazz, name, true);
            }
        }
    }

    @SneakyThrows
    public static <T> void setUserId(T bean) {
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipals().getPrimaryPrincipal();
        Class<?> aClass = bean.getClass();
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            if ("setUserId".equals(method.getName())) method.invoke(bean, user.getId());
        }
    }
}
